package models

import (
	"errors"
	"fmt"
	"github.com/astaxie/beego/orm"
	"reflect"
	"strings"
	"time"
)

type Book struct {
	Id          int       `orm:"column(id);pk;auto"`
	Name        string    `orm:"column(name);null"`
	Info        string    `orm:"column(info);null"`
	CreatedTime time.Time `orm:"column(created_time);auto_now_add;type(timestamp with time zone)"`
	//Author      *Author   `orm:"null;rel(fk)"`
}

func init() {
	orm.RegisterModel(new(Book))
}

func CreateBook(book *Book) (int64, error) {
	log.Debug("Function CreateBook input param:{book:%v}", book)
	o := orm.NewOrm()
	id, err := o.Insert(book)
	log.Debug("Function CreateBook return:{error:%v,Id:%v}", err, id)
	return id, err
}

func GetBooks(query map[string]string, fields []string, sortby []string, order []string, offset int64, limit int64) ([]interface{}, error) {
	log.Debug("Function GetBooks input param:{query:%v,fields:%v,sortby:%v,order:%v,offset:%v,limit:%v}", query, fields, sortby, order, offset, limit)
	o := orm.NewOrm()
	qs := o.QueryTable(new(Book))

	//query
	for k, v := range query {
		key := strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(key, v)
	}

	//order by
	var orderbys []string
	if len(sortby) > 0 {
		if len(sortby) == len(order) {
			for k, v := range sortby {
				if strings.ToUpper(order[k]) == "DESC" {
					orderbys = append(orderbys, fmt.Sprintf("-%v", v))
				} else if strings.ToUpper(order[k]) == "ASC" {
					orderbys = append(orderbys, v)
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
			}
		} else {
			if len(order) == 1 && len(sortby) > 1 {
				for _, v := range sortby {
					if strings.ToUpper(order[0]) == "DESC" {
						orderbys = append(orderbys, fmt.Sprintf("-%v", v))
					} else if strings.ToUpper(order[0]) == "ASC" {
						orderbys = append(orderbys, v)
					} else {
						return nil, errors.New("Error: Invalid order. Must be either [ASC|DESC]")
					}
				}
			} else {
				return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
			}
		}
	} else {
		if len(order) > 0 {
			return nil, errors.New("Error: unused 'order' fields")
		}
	}

	qs = qs.OrderBy(orderbys...)

	var books []Book
	var list []interface{}
	if _, err := qs.Limit(limit, offset).All(&books, fields...); err != nil {
		return nil, err
	}
	if len(fields) > 0 {
		for _, v := range books {
			m := make(map[string]interface{})
			val := reflect.ValueOf(v)
			for _, v2 := range fields {
				m[v2] = val.FieldByName(v2).Interface()
			}
			list = append(list, m)
		}
	} else {
		for _, v := range books {
			list = append(list, v)
		}
	}
	log.Debug("Function GetBooks return:{[]interface{}:%v}", list)
	return list, nil
}

func GetBookById(id int) (*Book, error) {
	log.Debug("Function GetBookById input param:{id:%v}", id)
	o := orm.NewOrm()
	book := &Book{Id: id}
	err := o.Read(book)
	if err == nil {
		log.Debug("Function GetBookById return:{*Book:%v}", book)
		return book, nil
	}
	return nil, err
}

func UpdateBookById(book *Book) error {
	log.Debug("Function UpdateBookById input param:{book:%v}", book)
	o := orm.NewOrm()
	b := Book{Id: book.Id}
	err := o.Read(&b)
	if err == nil {
		_, err := o.Update(book, "Name", "Info")
		log.Debug("Function UpdateBookById return:{error:%v}", err)
		return err
	}
	log.Debug("Function UpdateBookById return:{error:%v}", err)
	return err
}

func DeleteBookById(id int) error {
	log.Debug("Function DeleteBookById input param:{id:%v}", id)
	o := orm.NewOrm()
	b := Book{Id: id}
	err := o.Read(&b)
	if err == nil {
		_, err := o.Delete(&b)
		log.Debug("Function DeleteBookById return:{error:%v}", err)
		return err
	}
	log.Debug("Function DeleteBookById return:{error:%v}", err)
	return err
}
